
#include "common.hpp"
#include "transfer.hpp"
#include "aux.hpp"

#include <vector>
#include <cmath>
#include <cstdio>
#include <cstring>

#include <QtWi>
#include <QtWiTmpl>


///////////////////
// Gauge windows //
///////////////////

class Gauge
{
	// Defines to be used in derived gauge classes for gui
	
#define measure_declr(NAME) \
	ILabel *lbl_##NAME;\
	ITextBox *txt_##NAME;\
	ILabel *unit_##NAME;
	
#define measure(NAME, LABEL, UNIT) \
	measure_x(values, NAME, LABEL, UNIT)

#define measure_x(X, NAME, LABEL, UNIT)					\
	lbl_##NAME = ftory.makeLabel(X.widget(), LABEL, IGNORED_VAL, IGNORED_VAL);	\
	txt_##NAME = ftory.makeTextBox(X.widget(), "", IGNORED_VAL, IGNORED_VAL, ITextBox::Single); \
	unit_##NAME = ftory.makeLabel(X.widget(), UNIT, IGNORED_VAL, IGNORED_VAL);	\
	X.add_widget(lbl_##NAME, IDynamicWrapper<IRadioGroup>::BELOW); \
	X.add_widget(txt_##NAME, IDynamicWrapper<IRadioGroup>::RIGHTHAND);	\
	X.add_widget(unit_##NAME, IDynamicWrapper<IRadioGroup>::RIGHTHAND);

#define mutable_declr(NAME) \
	txt


protected:

	Ifactory &ftory;
	IWindow *win;
	IRawWidget *raw;
	
public:
	
	static const int gauge_window_width = 300;
	static const int gauge_update_interval;

private:
	double rate;
	int chunk;

protected:
	IMutexLock<double> rate_lock;
	IMutexLock<int> chunk_lock;
	
public:
	Gauge(Ifactory &f, const char *title, int x, int y, int h) :
		ftory(f), win(ftory.makeWindow(title, x, y)), raw(ftory.makeRawWidget(win, "", 0, 0)),
		rate(-1), chunk(-1),
		rate_lock(rate), chunk_lock(chunk)
		
	{
		// Fixme: Window height should be dynamic also
		win->resize(gauge_window_width + 20, h);
		raw->resize(gauge_window_width + 20, h);
		win->show();
		raw->show();
	}

	void setRate(double r)
	{
		rate_lock.set(r);
	}

	void setChunk(int c)
	{
		chunk_lock.set(c);
	}

	void invisible() const
	{
		win->hide();
	}

	void visible() const
	{
		win->show();
	}

		
	virtual void feedGaugeInfo(GaugeInfo *gi) = 0;
	
};


class TCPgauge : public Gauge, public IActionHandler, public ITimerHandler
{

private:
	
	// TCP measured values //
	IDynamicWrapper<IRadioGroup> values;

	measure_declr(rtt);
	measure_declr(jitter);
	measure_declr(maxrate);
	measure_declr(mss);
	measure_declr(throughput);
	measure_declr(sdelay);
	measure_declr(ploss);
	
	// TCP mutable/adaptable values //
	IDynamicWrapper<IRadioGroup> mutables;

	ICheckButton *window_scaling;
	ICheckButton *time_stamps;
	ICheckButton *mtu_discovery;
	ICheckButton *sack;
	ILabeledSlider window_frame;
	ILabeledSlider mtu;

	// Accessing the proc file system
	TCPIPnetworking nw;

	// Logfiles
	ValueLogger rtt;
	ValueLogger jitter;
	ValueLogger maxl;
	ValueLogger mssl;
	ValueLogger s11n;
	ValueLogger plsl;

	class EthDevLogger {

	private:
		FILE *handle;
		const char *path, *dev;
		ValueLogger packet_dev;
		ValueLogger bytes_dev;
		long bytes, packets;
		long init_bytes, init_packets;
	public:
		EthDevLogger(const char *dev) : path("/proc/net/dev"), dev(dev),
						  packet_dev("tcp/packets"), bytes_dev("tcp/rawtotal")
		{
			handle = fopen(path, "r");
			get();
			init_bytes = bytes;
			init_packets = packets;
		}

		~EthDevLogger()
		{
			fclose(handle);
		}
		
		void get()
		{
			char buf[512], name[128];

			if(!write_logfiles) return;
				
			if(!(handle = freopen(path, "r", handle)))
#ifdef DEBUG
				std::cerr << "cannot open " << path << std::endl;
#else
			;
#endif

			while(fgets(buf, sizeof buf, handle)) {
				sscanf(buf, "%s[^:]", name);
				if(!strncmp(dev, name, strlen(dev))) {
					if(sscanf(strchr(buf, ':') + 1, "%ld %ld", &bytes, &packets) != 2)
#ifdef DEBUG
						std::cerr << "failed reading from " << path<< std::endl;
#else
						;
#endif
				
				}
			}	
		}

		void log()
		{
			packet_dev.out() << packets - init_packets << std::endl;
			bytes_dev.out() << (bytes - init_bytes) * 8<< std::endl;	
		}
	
	} dev_logger;

public:
	
	TCPgauge(Ifactory &f, const char *title, int x, int y, int h) :
		Gauge(f, title, x, y, h),
		
		values(*win, f, x, y, gauge_window_width - 6 * general_widget_buffer),
		mutables(*win, f, x, y, gauge_window_width - 6 * general_widget_buffer),

		// todo: multiples of ... as steps
		window_frame(*mutables.widget(), ftory, "Frame", IGNORED_VAL, IGNORED_VAL, 235, 21, 0, 66000, 1000, 1, false),
		mtu(*mutables.widget(), ftory, "MTU", IGNORED_VAL, IGNORED_VAL, 235, 21, 0, 1500, 1000, 1, false),
		rtt("tcp/rtt"),
		jitter("tcp/jitter"),
		maxl("tcp/maxrate"),
		mssl("tcp/mss"),
		s11n("tcp/s11n"),
		plsl("tcp/loss"),
		dev_logger("eth0")
	{
		
		//// Values measured ////
		values.widget()->setCaption("Measurement");
		// RTT
		measure(rtt, "RTT", "ms");
		// Jitter (Instantaneous PDV)
		measure(jitter, "Jitter", "ms");
		// Max rate
		measure(maxrate, "Max rate", "bit/sec");
		// MSS (Maximum Segement Size)
		measure(mss, "MSS", "bytes");
		// Serialization delay
		measure(sdelay, "Serialization", "ms");
		// Packet loss probability
		measure(ploss, "Loss problt.", "%");
		
		values.draw(-25);
		values.widget()->show();
		
		//// Values to be set ////
		mutables.widget()->setCaption("Kernel Parameters");
		mutables.widget()->move(x, values.getHeight() + 2 * general_widget_buffer + 20);

		// TCP window frame scaling
		window_scaling = ftory.makeCheckButton(mutables.widget(), "Window Scaling", IGNORED_VAL, IGNORED_VAL);
		mutables.add_widget(window_scaling, IDynamicWrapper<IRadioGroup>::BELOW);

		// TCP time stamps
		time_stamps= ftory.makeCheckButton(mutables.widget(), "Timestamps", IGNORED_VAL, IGNORED_VAL);
		mutables.add_widget(time_stamps, IDynamicWrapper<IRadioGroup>::RIGHTHAND);

		// MTU discovery
		mtu_discovery= ftory.makeCheckButton(mutables.widget(), "MTU Discovery", IGNORED_VAL, IGNORED_VAL);
		mutables.add_widget(mtu_discovery, IDynamicWrapper<IRadioGroup>::BELOW);

		// Sack
		sack = ftory.makeCheckButton(mutables.widget(), "Sack", IGNORED_VAL, IGNORED_VAL);
		mutables.add_widget(sack, IDynamicWrapper<IRadioGroup>::RIGHTHAND);

		// Window frame size slider
		mutables.add_widget(&window_frame, IDynamicWrapper<IRadioGroup>::BELOW);
		ftory.getApp().registerAction(*window_frame.getBar(), IAction::MOD, this);

		// MTU slider
		mutables.add_widget(&mtu, IDynamicWrapper<IRadioGroup>::BELOW);
		ftory.getApp().registerAction(*mtu.getBar(), IAction::MOD, this);
		
		mutables.draw();

		// FIXME: (urgent)
		// We should not have to do this here; this, however is, because resize for TNW works only for the checkbutton itself
		// without the label, for Qt, on the other hand, this works for the whole label. QtWi should be fixed there.
#ifdef QTWI_TNW
		window_scaling->resize(general_checkbutton_edge, general_checkbutton_edge);
		time_stamps->resize(general_checkbutton_edge, general_checkbutton_edge);
		mtu_discovery->resize(general_checkbutton_edge, general_checkbutton_edge);
		sack->resize(general_checkbutton_edge, general_checkbutton_edge);
#endif

		// Set initial check box values
		window_scaling->setChecked(nw.getProc(TCPIPnetworking::WINDOW_SCALE).isSet());
		time_stamps->setChecked(nw.getProc(TCPIPnetworking::TIME_STAMP).isSet());
		mtu_discovery->setChecked(!nw.getProc(TCPIPnetworking::PMTU).isSet());
		sack->setChecked(nw.getProc(TCPIPnetworking::SACK).isSet());

		// Set initial scrollbar values
		std::string s = nw.getProc(TCPIPnetworking::RMAX).receive();
		window_frame.getLabel()->setText(s);
		window_frame.getBar()->setCurrent(string_convert<int>(s));
		s = nw.getProc(TCPIPnetworking::MTU).receive();
		mtu.getLabel()->setText(s);
		mtu.getBar()->setCurrent(string_convert<int>(s));
		
		// Register action handler
		ftory.getApp().registerAction(*window_scaling, IAction::CLICK, this);
		ftory.getApp().registerAction(*time_stamps, IAction::CLICK, this);
		ftory.getApp().registerAction(*mtu_discovery, IAction::CLICK, this);
		ftory.getApp().registerAction(*sack, IAction::CLICK, this);


		// NOTE: Too frequent interval kills TCP/IP kernel stack
		ftory.getApp().registerTimer(this, win, 5000);
		
		mutables.widget()->show();

	}
	


	// Handler for actions on slide bars or textboxes or checkbuttons
	virtual void handler(IWidget &w)
	{

		// FIXME: This is real bad programming technique applied here; not at all C++. Flog me, please.

		if(qtwi_rtti_cmp(w, ICheckButton)) {

			ICheckButton &b = dynamic_cast<ICheckButton&>(w);
			
			const char *caption = b.getCaption().c_str();

			if(!strcmp(caption, "Window Scaling")) {

				nw.getProc(TCPIPnetworking::WINDOW_SCALE).set(b.checked());

			} else if(!strcmp(caption, "Timestamps")) {

				nw.getProc(TCPIPnetworking::TIME_STAMP).set(b.checked());

			} else if(!strcmp(caption, "MTU Discovery")) {

				nw.getProc(TCPIPnetworking::PMTU).set(!b.checked());

			} else if(!strcmp(caption, "Sack")) {

				nw.getProc(TCPIPnetworking::SACK).set(b.checked());
				
			}
				
		} else if(qtwi_rtti_cmp(w, IScrollbar)) {

			// FIXME:
			// We have to handle this basic work here because the action handlers within
			// ILabeledSlider are somewhat buggy and thus disabled.
			std::string cur_frame = to_string<int>(window_frame.getBar()->current());
			window_frame.getLabel()->setText(cur_frame);
			std::string cur_mtu = to_string<int>(mtu.getBar()->current());
			mtu.getLabel()->setText(cur_mtu);
		}
	}

	/*
	 * Feed gauge with appropriate information collected from Connector, DataSource,
	 * and DataSink and calculate values - output them in their approrpiate text fields.
	 *
	 */
	void feedGaugeInfo(GaugeInfo *gi)
	{
		
		TCPGaugeInfo *ginf = static_cast<TCPGaugeInfo*>(gi);

		double rate_now = rate_lock.read();
		int chunk_now = chunk_lock.read();
		static double prev_rtt;
		
		double nrtt = ginf->rtt;
		txt_rtt->setText(nana(const_cast<char*>(to_string<double>(nrtt).c_str())));

		double jitt = fabs(nrtt - prev_rtt);
		
		txt_jitter->setText(nana(const_cast<char*>(to_string<double>(jitt).c_str())));
		prev_rtt = nrtt;
		
		// int bdp = (int)(rate_now * ginf->rtt / 1000); // bits/sec * sec = bits
		// todo: wscale?
		double maxr = (window_frame.getBar()->current() * 8) / (ginf->rtt / 1000);
		txt_maxrate->setText(maxr < 0 ? "0" : nana(const_cast<char*>(to_string<double>(rndn(maxr, 2)).c_str())));
				
		int mss = string_convert<int>(nw.getProc(TCPIPnetworking::MTU).receive()) - 40; // todo: constant
		txt_mss->setText(nana(const_cast<char*>(to_string<int>(mss).c_str())));

		double sdelay = (((double)chunk_now) * 8000.0) / rate_now;
		txt_sdelay->setText(nana(const_cast<char*>(to_string<double>(sdelay).c_str())));

		double ppls = 0;
		if(rate_now > 0) {
			double tmp = rate_now * nrtt / 1000;
			ppls = (mss * mss * 64) / (tmp * tmp);
			txt_ploss->setText(nana(const_cast<char*>(to_string<double>(rndn(100 * ppls, 2)).c_str())));
		} else {
			txt_ploss->setText("n/a");
		}
		
		// Write logfiles
		if(write_logfiles) {
			rtt.out() << nrtt << std::endl;
			jitter.out() << jitt << std::endl;
			maxl.out() << maxr << std::endl;
			mssl.out() << mss << std::endl;
			s11n.out() << sdelay << std::endl;
			plsl.out() << ppls << std::endl;
			dev_logger.get();
			dev_logger.log();
		}
		// Cleanup given GaugeInfo
		delete ginf;
	}

	//
	// Timer writes values to proc in intervals
	// Certain values, when being written to proc to frequently cause a system freeze, hence it is
	// done here in intervals.
	virtual void timeout()
	{
		static int prev_wf;
		static int prev_mtu;

		int wff = window_frame.getBar()->current();
		int mm = mtu.getBar()->current();
	
		if(prev_wf != wff) {
			// Set in and out window size simultaneously
			nw.getProc(TCPIPnetworking::RMAX).send(to_string<int>(window_frame.getBar()->current()).c_str());
			nw.getProc(TCPIPnetworking::SMAX).send(to_string<int>(window_frame.getBar()->current()).c_str());
		}

		if(prev_mtu != mm) {
			// Set MTU
			nw.getProc(TCPIPnetworking::MTU).send(to_string<int>(mtu.getBar()->current()).c_str());
		}
		
		prev_wf = wff;
		prev_mtu = mm;
	}
};


extern "C" {
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/unistd.h>
}

class FileGauge : public Gauge, public IActionHandler, public ITimerHandler
{

private:
	// Measured values //
	IDynamicWrapper<IRadioGroup> values;

	// File size
	measure_declr(size);

	// Mutable/adaptable values //
	IDynamicWrapper<IRadioGroup> mutables;

	measure_declr(maxsize);

public:
	static const long max_file_size;

	FileGauge(Ifactory &f, const char *title, int x, int y, int h) :
		Gauge(f, title, x, y, h),
		values(*win, f, x, y, gauge_window_width - 6 * general_widget_buffer),
		mutables(*win, f, x, y, gauge_window_width - 6 * general_widget_buffer)
		
	{

		values.widget()->setCaption("Measurement");
		measure(size, "File size", "");

		values.draw(20);
		values.widget()->show();
		
		mutables.widget()->setCaption("Mutables");
		mutables.widget()->move(x, values.getHeight() + 2 * general_widget_buffer + 20);
		
		measure_x(mutables, maxsize, "Max size", "bytes");
		txt_maxsize->setText(const_char(long, max_file_size));
		mutables.draw(20);
		mutables.widget()->show();
		
	}

	
	void handler(IWidget &w)
	{
		// add set maxsize button and monitor current size somehow, then (delete?) file
	}
		
	void feedGaugeInfo(GaugeInfo *gi)
	{
		FileGaugeInfo *fgi = static_cast<FileGaugeInfo*>(gi);
		
		struct stat st;

		if(lstat(fgi->path.c_str(), &st) < 0) {
			txt_size->setText("n/a");
		} else {
			txt_size->setText(nana(Connector::unit_convert(st.st_size, true).c_str()));
		}

		// Truncate if greater than max
		long max_size = string_convert<long>(txt_maxsize->text());

		if(st.st_size > max_size) {

			// FIXME: this is a non-working ftruncate workaround. we simply reopen fd.
			
			fgi->fd_lock.aquire();

			int fd;
			int flags = fcntl(*fgi->fd_lock, F_GETFD);

			close(*fgi->fd_lock);
			
			if((fd = open(fgi->path.c_str(), flags | O_TRUNC)) < 0) {
				ftory.getApp().affiliateWidget(*new IStandardMonologue(ftory, "Truncation", 30, 30, "Truncating file failed:\n" +
									   std::string(strerror(errno))));
			}

			fgi->fd_lock.unchecked_st(fd);
			
			fgi->fd_lock.release();
		}
		
		delete gi;
	}
	
};


extern "C" {
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <termios.h>	
}

// todo: backend

class SerialGauge : public Gauge, public IActionHandler, public ITimerHandler
{
private:
	// Measured values //
	IDynamicWrapper<IRadioGroup> values;

	// Symbol rate
	measure_declr(srate);


	// Baud //
	IDynamicWrapper<IRadioGroup> baud;
	IRadioButton *b9600, *b38400, *b115200;
	std::map<IRadioButton*, int> baud_map;

	// Data Framing //
	IDynamicWrapper<IRadioGroup> df;

	ILabeledSlider time;
	ILabeledSlider min;

	// Flags //
	IDynamicWrapper<IRadioGroup> flagss;
	ICheckButton *ignpar, *sflow, *hflow, *ofill, *ignbrk;

	// termios backend //

	struct termios current;
	
public:

	SerialGauge(Ifactory &f, const char *title, int x, int y, int h) :
		Gauge(f, title, x, y, h),
		values(*win, f, x, y, gauge_window_width - 6 * general_widget_buffer),
		baud(*win, f, x, y, gauge_window_width - 6 * general_widget_buffer),
		df(*win, f, x, y, gauge_window_width - 6 * general_widget_buffer),
		time(*df.widget(), ftory, "Time", IGNORED_VAL, IGNORED_VAL, 235, 21, 0, 250, 1000, 1, false),
		min(*df.widget(), ftory, "Min", IGNORED_VAL, IGNORED_VAL, 235, 21, 0, 250, 1000, 1, false),
		flagss(*win, f, x, y, gauge_window_width - 6 * general_widget_buffer)
	{
		values.widget()->setCaption("Measurement");
		measure(srate, "Symbol Rate", "Bd");
		values.draw(15);
		values.widget()->show();

		baud.widget()->setCaption("Baud");
		baud.widget()->move(x, values.getHeight() + 2 * general_widget_buffer + 20);

		b9600 = ftory.makeRadioButton(baud.widget(), "B9600", IGNORED_VAL, IGNORED_VAL);
		baud.add_widget(b9600, IDynamicWrapper<IRadioGroup>::BELOW);
		b38400 = ftory.makeRadioButton(baud.widget(), "B38400", IGNORED_VAL, IGNORED_VAL);
		baud.add_widget(b38400, IDynamicWrapper<IRadioGroup>::RIGHTHAND);
		b115200 = ftory.makeRadioButton(baud.widget(), "B115200", IGNORED_VAL, IGNORED_VAL);
		baud.add_widget(b115200, IDynamicWrapper<IRadioGroup>::RIGHTHAND);

		baud.draw(13);
		baud.widget()->show();

		df.widget()->setCaption("Raw Data Flow");
		df.add_widget(&time, IDynamicWrapper<IRadioGroup>::BELOW);
		df.add_widget(&min, IDynamicWrapper<IRadioGroup>::BELOW);
		
		df.widget()->move(x, values.getHeight() + baud.getHeight() + 2 * general_widget_buffer + 30);
		df.draw(15);
		df.widget()->show();

		flagss.widget()->setCaption("Flags");
		
		sflow = ftory.makeCheckButton(flagss.widget(), "Softflow control", IGNORED_VAL, IGNORED_VAL);
		hflow = ftory.makeCheckButton(flagss.widget(), "Hardflow control", IGNORED_VAL, IGNORED_VAL);
		ignpar = ftory.makeCheckButton(flagss.widget(), "Ignore parity errors", IGNORED_VAL, IGNORED_VAL);
		ofill = ftory.makeCheckButton(flagss.widget(), "Fill delays", IGNORED_VAL, IGNORED_VAL);
		ignbrk = ftory.makeCheckButton(flagss.widget(), "Ignore break conditions", IGNORED_VAL, IGNORED_VAL);
			
		flagss.add_widget(hflow, IDynamicWrapper<IRadioGroup>::BELOW);
		flagss.add_widget(sflow, IDynamicWrapper<IRadioGroup>::RIGHTHAND);
		flagss.add_widget(ignpar, IDynamicWrapper<IRadioGroup>::BELOW);
		flagss.add_widget(ofill, IDynamicWrapper<IRadioGroup>::RIGHTHAND);
		flagss.add_widget(ignbrk, IDynamicWrapper<IRadioGroup>::BELOW);
		
		flagss.widget()->move(x, values.getHeight() + baud.getHeight() + df.getHeight() + 2 * general_widget_buffer + 30);
		flagss.draw(15);
		flagss.widget()->show();

		// Register action handler
		ftory.getApp().registerAction(*time.getBar(), IAction::MOD, this);
		ftory.getApp().registerAction(*min.getBar(), IAction::MOD, this);

		// Termios
		
		baud_map[b9600] = B9600;
		baud_map[b38400] = B38400;
		baud_map[b115200] = B115200;

		b115200->setChecked(true);
	}

	void handler(IWidget&)
	{
		std::string cur_time = to_string<int>(time.getBar()->current());
		time.getLabel()->setText(cur_time);

		std::string cur_min = to_string<int>(min.getBar()->current());
		min.getLabel()->setText(cur_min);
	}

	void feedGaugeInfo(GaugeInfo* gi)
	{
		SerialGaugeInfo *sgi = static_cast<SerialGaugeInfo*>(gi);
		IMutexLock<int, PTHREAD_MUTEX_RECURSIVE> &serial_fd_lock = sgi->fd_lock;
		
		static bool init = false, error = false;

		usleep(100000);
		
		int fd = serial_fd_lock.read();

		// Initial setup
		if(!init) {

			// todo: kill connector
			
			if(error) return;
			
			memset(&current, 0, sizeof(struct termios));

			std::cout << "FD_gauge: " << fd << std::endl;

			int r;
			if((r = tcgetattr(fd, &current)) != 0) {
				
				ftory.getApp().affiliateWidget(*new IStandardMonologue(ftory, "Serial Port", 30, 30,
										       "Unable to obtain serial port information:\n" +
										       std::string(strerror(errno))));
				error = true;
#ifdef DEBUG
				perror("tcgetattr()");
#endif
				
				return;
			}
			
			current.c_cflag = CS8 | CLOCAL | CREAD;
			current.c_iflag = ICRNL;
			current.c_oflag = 0;

			// Initial checkbox values
			hflow->setChecked(current.c_cflag & CRTSCTS);
			
			if(current.c_iflag & (IXON | IXOFF | IXANY)) {
				current.c_iflag |= (IXON | IXOFF | IXANY);
				sflow->setChecked(true);
			} else {
				sflow->setChecked(false);
				current.c_iflag &= ~(IXON | IXOFF | IXANY);
			}

			ignpar->setChecked(current.c_iflag & IGNPAR);
			ofill->setChecked(current.c_oflag & OFILL);
			ignbrk->setChecked(current.c_iflag & IGNBRK);

			// Flow
			time.setCurrent(current.c_cc[VTIME]);
			min.setCurrent(current.c_cc[VMIN]);
			
			++init;
		}

		current.c_cflag &= ~(B9600 | B38400 | B115200);

		// Set baud rate
		if(b9600->checked())
			current.c_cflag |= baud_map[b9600];
		else if(b38400->checked())
			current.c_cflag |= baud_map[b38400];
		else
			current.c_cflag |= baud_map[b115200];

		
		// Flags
		if(hflow->checked())
			current.c_cflag |= CRTSCTS;
		else
			current.c_cflag &= ~CRTSCTS;


		if(sflow->checked())
			current.c_iflag |= (IXON | IXOFF | IXANY);
		else
			current.c_iflag &= ~(IXON | IXOFF | IXANY);
		
		if(ignpar->checked())
			current.c_iflag |= IGNPAR;
		else
			current.c_iflag &= ~IGNPAR;

		if(ofill->checked())
			current.c_oflag |= OFILL;
		else
			current.c_oflag &= ~OFILL;

		if(ignbrk->checked())
			current.c_iflag |= IGNBRK;
		else
			current.c_iflag &= ~IGNBRK;

		// Set and log symbol rate
		int sr = (int)(rate_lock.read() / 7);
		txt_srate->setText(nana(const_cast<char*>(to_string<int>(sr).c_str())));

		if(tcsetattr(fd, TCSANOW, &current) < 0) {
			ftory.getApp().affiliateWidget(*new IStandardMonologue(ftory, "Serial Port", 30, 30, "Unable to set serial port."));
		}

	}
	
};

// FIXME:
// This wrapper is to cirumvent a bug caused by some weird
// callback function pointer mess up withing GCC. It handles the actions
// ILabeledSlider would normally receive direcly and handle.
class ILabeledSliderWrapper : public IActionHandler
{
private:
	Ifactory &ftory;
	ILabeledSlider lbls;
public:

	void handler(IWidget &w)
	{
		std::cout << "stub\n";
		
	}

	ILabeledSliderWrapper(IWidget &p, Ifactory &f, const char *cap,
			      int x, int y, int w, int h, int min, int max, int step, int page) :
		ftory(f), lbls(p, f, cap, x, y, w, h, min, max, step, page, false)
	{		
			ftory.getApp().registerAction(*lbls.getBar(), IAction::MOD, this);
	}

	// Sorry, cannot use virtual functions in this rather messed up case
	void resize(int w, int h)
	{
		lbls.resize(w, h);
	}

	void move(int x, int y)
	{
		lbls.move(x, y);
	}

	void show()
	{
		lbls.show();
	}

};
